#ifndef FLASHROS_INFERENCE_PIPELINE_FACTORY_H
#define FLASHROS_INFERENCE_PIPELINE_FACTORY_H

#include "INIReader.h"
#include "flashros/inference/input_pipeline.h"
#include "flashros/inference/output_pipeline.h"

namespace flashros {
namespace inference {

enum InputPipelineType { VIDEO_INPUT = 0, ROS_INPUT = 1 };

class InputPipelineFactory {
 public:
  static InputPipelineFactory& GetInstance() {
    static InputPipelineFactory factory;
    return factory;
  }

  InputInterface* GetInputPipeline(INIReader& reader, ros::NodeHandle& nh,
                                   std::string data_root = "") {
    auto input_pipeline_type =
        (InputPipelineType)reader.GetInteger(section_, "type", -1);
    switch (input_pipeline_type) {
      case VIDEO_INPUT:

        return new VideoCaptureInput(
            data_root + reader.GetString(section_, "video_path", ""), nh);
      case ROS_INPUT:
        return new RosInput(reader.GetString(section_, "camera_topic_name", ""),
                            reader.GetInteger(section_, "max_wait_secs", 500),
                            nh);
    }

    SPDLOG_ERROR("Input Pipeline Type {}", input_pipeline_type);
    return NULL;
  }

  InputPipelineFactory(const InputPipelineFactory&) = delete;
  InputPipelineFactory& operator=(const InputPipelineFactory&) = delete;

 private:
  InputPipelineFactory() = default;
  const std::string section_ = "input";
};

enum OutputPipelineType { VISUALIZE_OUTPUT = 0, ROS_OUTPUT = 1 };

class OutputPipelineFactory {
 public:
  static OutputPipelineFactory& GetInstance() {
    static OutputPipelineFactory factory;
    return factory;
  }

  DetectionOutputInterface* GetDetectionOutputInterface(
      INIReader& reader, ros::NodeHandle& nh, std::string data_root = "") {
    auto output_pipeline_type =
        (OutputPipelineType)reader.GetInteger(section_, "type", -1);
    switch (output_pipeline_type) {
      case VISUALIZE_OUTPUT:
        return new VisualizeDetectionOutput(reader, section_, nh, data_root);
      case ROS_OUTPUT:
        return new RosDetectionOutput(
            reader.GetString(section_, "detection_topic_name", ""), nh);
    }

    SPDLOG_ERROR("Output Pipeline Type {}", output_pipeline_type);
    return NULL;
  }

  OutputPipelineFactory(const OutputPipelineFactory&) = delete;
  OutputPipelineFactory& operator=(const OutputPipelineFactory&) = delete;

 private:
  OutputPipelineFactory() = default;
  const std::string section_ = "output";
};

}  // namespace inference
}  // namespace flashros

#endif  // FLASHROS_INFERENCE_PIPELINE_FACTORY_H